Перейти к основному содержимому

3.06. Cassandra

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Cassandra

Apache Cassandra — это распределённая, децентрализованная, open-source система управления данными, относящаяся к классу wide-column stores (ширококолоночных хранилищ) в рамках фамилии NoSQL-решений. Она была спроектирована с нуля для обеспечения высокой доступности, линейной масштабируемости и отказоустойчивости даже в условиях выхода из строя целых дата-центров. Ключевая особенность Cassandra — её архитектура без единой точки отказа: в кластере нет выделенного мастера, все узлы равноправны и могут обслуживать как операции чтения, так и записи. Эта модель, известная как masterless architecture, позволяет Cassandra функционировать без перерывов даже при потере значительной части инфраструктуры, при этом гарантируя сохранность данных.

Cassandra не является ни улучшенной реляционной СУБД, ни упрощённым key-value хранилищем. Это самостоятельная парадигма, возникшая в ответ на конкретные требования высоконагруженных систем, в первую очередь — Facebook (где Cassandra и была разработана в 2008 году как часть инфраструктуры почтового сервиса). В 2010 году проект был передан Apache Software Foundation и с тех пор развивается как полностью независимый open-source продукт, управляемый сообществом и корпоративными участниками (в частности, DataStax, ScyllaDB, Instaclustr и другими).

Контекст

Для понимания места Cassandra в экосистеме СУБД необходимо рассмотреть эволюцию требований к системам хранения данных. Традиционные реляционные базы данных (PostgreSQL, MySQL, Oracle) великолепно справляются с работой в рамках одного сервера: обеспечивают строгую целостность (ACID), поддерживают сложные связи между сущностями, транзакции, JOIN’ы и декларативные запросы. Однако при попытке масштабирования за пределы одного узла они сталкиваются с фундаментальными ограничениями: горизонтальное масштабирование затруднено, репликация часто асинхронна и может приводить к расхождениям, а централизованная архитектура создаёт узкие места и точки отказа.

Парадигма NoSQL возникла как реакция на потребности веб-гигантов (Google, Amazon, Facebook), которым требовались системы, способные:

  • обрабатывать миллиарды операций в секунду;
  • хранить экза- и зеттабайты данных;
  • выдерживать отказы оборудования без простоев;
  • обеспечивать низкие задержки даже при географически распределённой инфраструктуре;
  • поддерживать «мягкую» схему данных, допускающую эволюцию структуры без дорогостоящих миграций.

В рамках NoSQL выделяют четыре основные модели данных:

  • Key-Value (Redis, DynamoDB);
  • Document (MongoDB, Couchbase);
  • Graph (Neo4j, JanusGraph);
  • Wide-Column (Cassandra, ScyllaDB, HBase).

Cassandra относится к последней группе. Её отличительная черта — организация данных по колонкам, причём не в том смысле, в каком это делает, например, ClickHouse (речь о котором пойдёт в отдельной главе), а в виде гибкой, иерархической структуры, где строки могут иметь различный набор атрибутов, а физическое хранение данных оптимизировано под последовательное чтение целых столбцов или групп столбцов.

На уровне реализации данные группируются в sstables, и внутри них информация упорядочена по partition key, затем по clustering key, что позволяет эффективно обрабатывать запросы, фильтрующие по первичному ключу и его компонентам. Термин «колоночная» здесь отражает логическую модель данных — хотя и физическое хранение всё же использует преимущества колоночного доступа при сканировании sstable’ов.

Логическая модель данных

Cassandra оперирует понятием keyspace — аналогом схемы (schema) в реляционных СУБД. Каждый keyspace содержит одну или несколько таблиц (ранее называвшихся column families), но эти таблицы принципиально отличаются от SQL-таблиц.

В реляционной модели таблица — это строгий набор строк фиксированной структуры: каждая строка содержит одинаковый набор столбцов, определённый в DDL. В Cassandra таблица — это упорядоченное множество строк, в каждой из которых может быть различное количество атрибутов, причём структура определяется через первичный ключ и дополнительные столбцы, которые могут быть опущены.

Каждая строка идентифицируется разделительным ключом (partition key). Это может быть один столбец или комбинация столбцов (composite key). Все данные, принадлежащие одной партиции (то есть имеющие одинаковое значение partition key), хранятся физически рядом — на одном или нескольких узлах кластера, в зависимости от стратегии репликации. Внутри партиции строки дополнительно упорядочиваются по ключу кластеризации (clustering key) — ещё одному набору столбцов, который задаёт сортировку внутри партиции.

Пример:

CREATE TABLE user_events (
user_id UUID,
event_time TIMESTAMP,
event_type TEXT,
payload TEXT,
PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

Здесь:

  • user_id — partition key;
  • event_time — clustering key;
  • В рамках одной партиции (например, user_id = a1b2c3) строки будут храниться упорядоченно по убыванию времени события;
  • Для одного user_id может быть сколько угодно событий — новых строк, каждая со своим event_time;
  • У разных пользователей может быть совершенно разное количество событий — от нуля до миллиардов;
  • Структура строки не фиксирована: можно добавить столбцы device_id, ip_address и т.д. без ALTER TABLE в будущем, просто начав вставлять значения — Cassandra не проверяет их наличие во всех строках.

Такая гибкость достигается за счёт отказа от строгой нормализации и межстрочных связей. В Cassandra данные дублируются намеренно: одна и та же сущность может храниться в нескольких таблицах с разной структурой, оптимизированной под конкретные запросы. Это называется денормализацией. Здесь проектирование базы данных начинается с запросов: сначала определяют, какие операции будут выполняться, затем — как устроить таблицы, чтобы каждый запрос обращался к одной партиции (или минимальному их числу). JOIN’ы, подзапросы и транзакции, охватывающие несколько партиций — отсутствуют по дизайну.

Архитектура

Cassandra реализует модель peer-to-peer (P2P). Каждый узел (node) в кластере знает о других узлах через механизм gossip protocol — лёгкое, периодическое обменное взаимодействие, в ходе которого узлы обмениваются информацией о своём состоянии, о состоянии других узлов и о топологии кластера. Это позволяет кластеру динамически адаптироваться: узел может быть добавлен или удалён без остановки системы и без перенастройки клиентов.

Данные распределяются по узлам с помощью consistent hashing с применением partitioner (по умолчанию — Murmur3Partitioner). Пространство ключей (token ring) делится на равные сегменты, и каждому узлу назначается набор токенов (в современных версиях — один токен без использования vnodes, хотя vnodes поддерживаются для совместимости). При записи данные сначала попадают в commit log (на диск, для durability) и memtable (в оперативную память, для скорости). Когда memtable достигает порога, она сбрасывается на диск в виде sstables — неизменяемых (immutable), упорядоченных файлов. Со временем sstables компактифицируются (compaction), чтобы удалить дубликаты и устаревшие версии записей (в том числе — помеченные как удалённые, tombstones).

Репликация производится на основе replication factor (RF) — числа копий данных, которые должны храниться в кластере. Например, RF = 3 означает, что каждая партиция будет храниться на трёх узлах. Выбор узлов для репликации определяется replication strategy: SimpleStrategy (для одного дата-центра) или NetworkTopologyStrategy (для нескольких дата-центров, с контролем количества реплик в каждом). Это позволяет размещать реплики так, чтобы выдержать отказ целого дата-центра без потери данных и доступности.

Консистентность (consistency level) выбирается на уровне запроса — клиент сам решает, сколько узлов должно подтвердить операцию, чтобы она считалась успешной. Например:

  • ONE — подтверждение от одного узла;
  • QUORUM — от большинства узлов, хранящих данную партицию (обычно (RF / 2) + 1);
  • ALL — от всех реплик.

Такой подход позволяет гибко балансировать между скоростью, доступностью и строгостью консистентности (в рамках CAP-теоремы Cassandra тяготеет к AP — Availability и Partition tolerance, при этом позволяя приблизиться к C за счёт настройки уровней консистентности).

Экосистема и инструментарий Apache Cassandra

Apache Cassandra — это ядро СУБД и зрелая экосистема, включающая официальные и сообщественные инструменты для развертывания, мониторинга, администрирования, интеграции и разработки. Экосистема формировалась в течение более чем десяти лет и включает как open-source, так и коммерческие решения.

Ядро: Apache Cassandra и его развитие

Основной дистрибутив — Apache Cassandra — поддерживается Apache Software Foundation. Это полностью open-source проект с лицензией Apache 2.0. Развитие происходит через процесс Apache Community Development: предложения вносятся в виде CASSANDRA JIRA тикетов, обсуждаются на mailing lists и принимаются после ревью коммиттеров. С момента выхода Cassandra 4.0 в 2021 году была значительно усилена стабильность, безопасность (поддержка SSL/TLS на уровне клиента и сервера, аудит-логгирование), производительность (Zero Copy Streaming для быстрой перебалансировки) и тестируемость (включая воспроизведение production-нагрузок через fqltool).

Важно отметить, что Cassandra следует принципу long-term stability: новые функции вносятся осторожно, с акцентом на обратную совместимость. Согласно текущей политике, одновременно поддерживаются до четырёх основных версий (например, 3.11.x, 4.0.x, 4.1.x, 5.0.x), что даёт предприятиям гибкость при планировании обновлений.

Дистрибутивы и платформы на основе Cassandra

Помимо «голого» Apache Cassandra, существуют производные реализации и управляемые сервисы:

  • DataStax Astra DB — fully managed серверлесс-сервис Cassandra-as-a-Service, совместимый с CQL, с REST/gRPC API, встроенной аутентификацией и веб-интерфейсом. Позволяет запускать кластеры в облаках AWS, GCP и Azure без управления инфраструктурой. Поддерживает Vector Search (начиная с 2024 г.), что расширяет применение Cassandra в ML-сценариях.

  • ScyllaDB — drop-in замена Cassandra на C++, переписанная с нуля с акцентом на максимальную производительность. Использует асинхронную архитектуру Seastar, что позволяет добиваться в разы большей пропускной способности при меньшей задержке. Сохраняет совместимость с CQL, SSTable-форматом и большинством драйверов. Особенно эффективна на современных SSD/NVMe и многопроцессорных системах.

  • K8ssandra — проект, объединяющий Cassandra с Kubernetes. Включает Helm-чарты, оператор Cassandra, инструменты мониторинга (Prometheus, Grafana), резервного копирования (Medusa), восстановления и управления. Позволяет разворачивать Cassandra-кластеры как cloud-native приложения.

  • Instaclustr, Aiven, Amazon Keyspaces (for Apache Cassandra) — управляемые сервисы от сторонних провайдеров. Keyspaces, например, предлагает API-совместимость с Cassandra, но не использует исходный код Apache Cassandra — это проприетарная реализация от AWS, оптимизированная под их инфраструктуру.

Эти решения расширяют применение в разных контекстах: от bare-metal кластеров до serverless-архитектур.


Инструменты администрирования и разработки

Хотя базовый интерфейс Cassandra — консольный (cqlsh), на практике разработчики и администраторы используют графические и скриптовые инструменты.

cqlsh — официальная консоль Cassandra Query Language

cqlsh — это Python-клиент, поставляемый с дистрибутивом Cassandra. Он предоставляет интерактивную оболочку для выполнения CQL-команд, просмотра метаданных (DESCRIBE KEYSPACES, DESCRIBE TABLE users), выполнения запросов и отладки. Поддерживает подсветку синтаксиса, автодополнение и историю. Для продвинутых сценариев используется в скриптах (cqlsh -f script.cql).

Графические клиенты

  • DataStax DevCenter (устаревший, но всё ещё используемый) — desktop-приложение с поддержкой подключения к нескольким кластерам, визуальным редактором схем, профилированием запросов и историей выполнения. Совместим с Apache Cassandra, не требует DataStax Enterprise.

  • DBeaver — универсальный SQL-клиент с поддержкой Cassandra через официальный Java-драйвер. Поддерживает просмотр структуры keyspace’ов, выполнение запросов, экспорт данных. Удобен при работе с гетерогенной инфраструктурой (PostgreSQL + Cassandra + Kafka и т.д.).

  • Cassandra Reaper — open-source инструмент для управления repair — фоновым процессом синхронизации реплик, критически важным для консистентности в распределённой системе. Позволяет планировать, мониторить и приостанавливать repair-сессии с веб-интерфейсом.

  • Prometheus + Grafana + Cassandra Exporter — стандартный стек для мониторинга. Cassandra предоставляет JMX-метрики; экспортер преобразует их в формат Prometheus. Готовые дашборды от сообщества отслеживают: latency, throughput, compaction backlog, cache hit ratio, thread pool utilisation и другие ключевые показатели.

  • Medusa — инструмент для бэкапа и восстановления на основе snapshot’ов и sstable-копирования. Поддерживает интеграцию с облаками (S3, GCS), шифрование, инкрементальные бэкапы.


Программные интерфейсы: драйверы и фреймворки

Для интеграции Cassandra в приложения используются официальные и third-party драйверы. Все они реализуют протокол Cassandra Native Protocol (начиная с v1 в 2012 г., сейчас актуален v5/v6), который бинарный, асинхронный и поддерживает connection pooling, подготовленные запросы, устойчивость к отказам.

Официальные драйверы (DataStax)

DataStax, несмотря на коммерческую ориентацию, поддерживает open-source драйверы, которые совместимы с Apache Cassandra:

  • Java Driver (com.datastax.oss:java-driver-core) — эталонная реализация. Поддерживает реактивные стримы (Reactor, RxJava), автоматическое обнаружение топологии кластера, политики retry и speculative execution. Интегрируется с Spring через Spring Data Cassandra, предоставляя шаблонный доступ (CassandraTemplate) и репозитории (CrudRepository-аналоги).

  • Python Driver (cassandra-driver) — асинхронный (через asyncio или gevent) и синхронный интерфейсы. Поддерживает подготовленные запросы, connection pooling, автоматическую маршрутизацию по локальным дата-центрам.

  • Node.js Driver (@datastax/cassandra-driver) — построен на async/await, поддерживает streaming-результатов, политики балансировки нагрузки.

  • C#/.NET Driver (CassandraCSharpDriver) — полностью async/await, с поддержкой LINQ-подобных выражений через LinqProvider.

Все драйверы реализуют token-aware routing: запрос направляется непосредственно на узел, ответственный за нужную партицию, минуя промежуточные ноды — это снижает latency и нагрузку на сеть.

Альтернативные и сообщественные драйверы

  • gocql — популярный драйвер для Go, высокооптимизированный, с поддержкой кастомных типов и tracing’а.
  • php-driver — расширение на C для PHP, с интерфейсом, близким к PDO.
  • rust-cassandra-driver — нативная реализация на Rust, активно развивается, подходит для high-performance микросервисов.

Важный момент: драйверы не эмулируют SQL-семантику. Они работают строго в рамках возможностей Cassandra: отсутствие JOIN’ов, ограничения на фильтрацию (только по компонентам первичного ключа без ALLOW FILTERING), отсутствие глобальных транзакций.


Cassandra Query Language (CQL): SQL-подобный, но не SQL

CQL — это декларативный язык запросов, разработанный специально для Cassandra. Он сознательно сделан похожим на SQL, чтобы снизить порог входа для разработчиков с реляционным опытом. Однако это аналогия на синтаксическом уровне, а не семантическая совместимость.

Синтаксические параллели и ключевые различия

КонструкцияВ SQL (PostgreSQL/MySQL)В CQL (Cassandra)
Создание схемыCREATE SCHEMA или CREATE DATABASECREATE KEYSPACE
ТаблицаCREATE TABLE users (id INT PRIMARY KEY, ...)CREATE TABLE users (id UUID, ..., PRIMARY KEY (id))
Первичный ключМожет быть составным, но не управляет физ. расположениемОбязательно состоит из PARTITION KEY + (опц.) CLUSTERING COLUMNS; определяет, как данные распределяются и сортируются
SELECTПоддерживает JOIN, подзапросы, оконные функцииТолько одна таблица. Фильтрация — только по компонентам первичного ключа (или через ALLOW FILTERING, что не рекомендуется). Нет подзапросов.
UPDATE/DELETEМожет затрагивать любые строки по WHEREБез WHERE, содержащего полный PARTITION KEY, операция недопустима (или требует ALLOW FILTERING с риском full scan)
ТранзакцииBEGIN; … COMMIT; с ACID-гарантиямиНет multi-row транзакций. Есть lightweight transactions (LWT) на основе Paxos — IF NOT EXISTS, IF column = value, но с высокой latency и нагрузкой
ИндексыB-tree, Hash, GiST, GIN и т.д.Вторичные индексы (CREATE INDEX) поддерживаются, но неэффективны при высокой кардинальности; материаллизованные представления устарели; рекомендуется денормализация

Пример типичного CQL-запроса:

SELECT event_type, payload 
FROM user_events
WHERE user_id = a1b2c3d4
AND event_time > '2025-01-01 00:00:00'
AND event_time < '2025-02-01 00:00:00';

Здесь:

  • user_id — partition key → запрос идёт к конкретной партиции;
  • event_time — clustering key → фильтрация внутри партиции выполняется эффективно (по упорядоченному индексу);
  • Ответ возвращается в порядке, заданном CLUSTERING ORDER BY.

Если попытаться выполнить:

SELECT * FROM user_events WHERE event_type = 'login';

— запрос завершится ошибкой, если не добавить ALLOW FILTERING. С этой опцией он выполнится, но будет сканировать все партиции — что на кластере в миллионы строк может занять часы и убить производительность. Это демонстрирует фундаментальный принцип: модель данных строится под запросы, а не наоборот.


Сравнение с другими системами хранения: где Cassandra уместна — и где нет

КритерийCassandraPostgreSQL / MySQLClickHouseMongoDB
Модель данныхWide-column (гибкие партиции)Relational (строгая схема)Columnar (реляционная, но физически — колонки)Document (BSON, вложенные структуры)
АрхитектураMasterless, P2PMaster-replica / Patroni / CitusDistributed (шардирование + репликация)Replica set / Sharded cluster
Консистентность по умолчаниюTuneable (ONE, QUORUM, ALL)Strong (ACID)Eventual (но с фоновой repair’кой)Strong (в рамках replica set)
Запись vs ЧтениеОптимизирована под записьСбалансированаОптимизирована под агрегацию и чтениеСбалансирована
МасштабированиеЛинейное, горизонтальное, без downtimeСложное (шардирование — отдельный слой)Линейное, но с ограничениями на UPDATE/DELETEГоризонтальное (шарды)
Поддержка JOIN’овНетПолнаяПоддержка, но неэффективнаLookup’ы через $lookup, но локально
Гео-репликацияВстроенная, на уровне стратегииЧерез логическую репликацию / BDRНет в core; требуется внешний оркестраторПоддержка multi-region
Типичные сценарииЛогирование, IoT, телеметрия, инвентарь, временные ряды (с ограничениями)OLTP, финансовые системы, ERPOLAP, аналитика, отчёты в реальном времениКонтент, каталоги, пользовательские профили

Важное уточнение по ClickHouse:
ClickHouse — это реляционная СУБД с колоночным физическим хранением. Она поддерживает SQL (в расширенном диалекте), JOIN’ы, подзапросы, но оптимизирована для massive parallel reads и агрегации. В отличие от Cassandra, в ClickHouse:

  • данные не предназначены для частых UPDATE/DELETE (изменения — фоновые, пакетные);
  • первичный ключ — для сортировки на диске (sparse index);
  • нет встроенной отказоустойчивости «из коробки» — репликация требует кластерной настройки.

Таким образом, Cassandra и ClickHouse решают разные задачи: первая — для записи и хранения потоков событий с гарантией доступности; вторая — для анализа этих событий позже.


Типичные сценарии применения Cassandra

Cassandra наиболее эффективна в доменах, где доминируют следующие требования:

  1. Высокая скорость записи с гарантией durability
    Пример: телеметрия IoT-устройств. Датчики отправляют миллионы событий в секунду. Каждое событие — строка в таблице sensor_readings(partition_key = sensor_id, clustering_key = timestamp). Cassandra принимает поток без бутылочных горлышек, даже при выходе из строя части узлов.

  2. Глобальная доступность и tolerance к сетевым разрывам
    Пример: мессенджер или социальная сеть с пользователями по всему миру. Репликация настраивается так: 2 копии в Европе, 2 — в США, 2 — в Азии (RF = 6). При обрыве трансатлантического кабеля пользователи в Азии продолжают работать — их данные локально доступны.

  3. Хранение временных рядов с высокой кардинальностью
    Пример: метрики приложений (latency, error rate по микросервисам). Модель metrics(service, host, timestamp) PRIMARY KEY ((service, host), timestamp) позволяет эффективно выбирать «все метрики за час по сервису X на хосте Y». Важно: Cassandra не заменяет TimescaleDB или InfluxDB для сложных временных запросов, но отлично подходит для «сырых» данных.

  4. Каталоги и инвентарные системы
    Пример: реестр устройств в телеком-сети (миллионы SIM-карт, роутеров). Записи редко изменяются, но часто читаются. Денормализация позволяет хранить одну сущность в нескольких представлениях: по iccid, по msisdn, по customer_id — без JOIN’ов.

  5. Сессионные хранилища и кэши с персистентностью
    Пример: распределённый сессионный store для stateless-микросервисов. TTL (time-to-live) на уровне столбца позволяет автоматически удалять устаревшие сессии.

Где Cassandra не рекомендуется

  • OLTP-системы с жёсткими требованиями к транзакциям (банковские переводы, бухгалтерия);
  • Приложения, где доминируют сложные JOIN’ы и ad-hoc аналитические запросы;
  • Системы с низким объёмом данных — накладные расходы на кластер не оправданы;
  • Сценарии, требующие полнотекстового поиска — лучше интегрировать с Elasticsearch, а не пытаться эмулировать его в Cassandra.